package com.vhall.component.service.vote.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.vhall.component.dao.account.AccountMapper;
import com.vhall.component.dao.vote.RoomVoteLkMapper;
import com.vhall.component.dao.vote.VoteAnswersMapper;
import com.vhall.component.dao.vote.VotesMapper;
import com.vhall.component.entity.account.AccountEntity;
import com.vhall.component.entity.export.ExportEntity;
import com.vhall.component.entity.page.vo.PageVO;
import com.vhall.component.entity.room.dto.RoomJoinsEntityDTO;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.entity.vote.RoomVoteLkEntity;
import com.vhall.component.entity.vote.VoteAnswersEntity;
import com.vhall.component.entity.vote.VotesEntity;
import com.vhall.component.entity.vote.vo.*;
import com.vhall.component.entity.vote.vo.console.StatReqVO;
import com.vhall.component.entity.vote.vo.console.StatRsqVO;
import com.vhall.component.entity.vote.vo.console.VoteExportVO;
import com.vhall.component.entity.vote.vo.console.VotesUpdateConsoleRepVO;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.constant.RoomConstant;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.support.Token;
import com.vhall.component.plugin.common.support.TokenThreadLocal;
import com.vhall.component.plugin.common.utils.DateUtils;
import com.vhall.component.service.export.ExportService;
import com.vhall.component.service.room.RoomInnerService;
import com.vhall.component.service.vote.VoteConsoleService;
import com.vhall.component.service.vote.VoteConstants;
import com.vhall.component.service.vote.VoteService;
import com.vhall.component.service.vote.VotesRepository;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author longzhen.gong
 * @date 2021/7/6 19:19
 */
@Service
public class VoteConsoleServiceImpl implements VoteConsoleService {

    @Autowired
    private RoomVoteLkMapper roomVoteLkMapper;

    @Autowired
    private RoomInnerService roomInnerService;

    @Autowired
    private VotesMapper votesMapper;

    @Autowired
    private VoteService voteService;

    @Autowired
    private VoteAnswersMapper voteAnswersMapper;

    @Autowired
    private ExportService exportService;

    private static final String VOTE_ID_COLUMN = "vote_id";
    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private VotesRepository votesRepository;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public VotesCreateRspVO create(VotesCreateReqVO reqVO) {
        Long voteId = reqVO.getVoteId();
        if (votesRepository.getById(voteId) != null) {
            throw new BusinessException(BizErrorCode.COMP_VOTE_INVALID);
        }
        try {
            JSON.parseArray(reqVO.getExtend());
        } catch (Exception e) {
            throw new BusinessException(BizErrorCode.COMP_VOTE_EXTEND);
        }
        //缺少与sdk保持数据一致的校验
        VotesEntity votesEntity = new VotesEntity();
        votesEntity.setAccountId(reqVO.getAccountId());
        votesEntity.setVoteId(reqVO.getVoteId());
        votesEntity.setExtend(reqVO.getExtend());
        votesEntity.setIsPublic(VoteConstants.RoomVoteLkBind.PUBLIC_YES);
        votesEntity.setLimitTime(reqVO.getLimitTime());
        votesEntity.setOptionNum(reqVO.getOptionNum());
        votesEntity.setTitle(reqVO.getTitle());
        votesEntity.setCreatedAt(LocalDateTime.now());
        votesEntity.setUpdatedAt(LocalDateTime.now());
        votesRepository.save(votesEntity);
        votesEntity = votesRepository.getById(voteId);
        if (StringUtils.isNotBlank(reqVO.getRoomId())) {
            voteService.bindRoom(voteId, reqVO.getRoomId());
        }
        VotesCreateRspVO createRspVO = new VotesCreateRspVO();
        BeanUtils.copyProperties(votesEntity, createRspVO);
        return createRspVO;
    }

    @Override
    public List<Long> delete(List<Long> voteIds, Integer accountId) {
        //查询删除投票中是否有已发布未结束投票
        int count = roomVoteLkMapper.selectCount(Wrappers.<RoomVoteLkEntity>lambdaQuery()
                .eq(RoomVoteLkEntity::getPublish, VoteConstants.RoomVoteLkBind.PUBLISH_YES)
//                .eq(RoomVoteLkEntity::getIsFinish, VoteConstants.RoomVoteLkBind.UNFINISH)
                .in(RoomVoteLkEntity::getVoteId, voteIds)
        );
        if (count > 0) {
            throw new BusinessException(BizErrorCode.COMP_VOTE_DEL_FAIL);
        }
        List<Long> result = new ArrayList<>();
        for (Long voteId : voteIds) {
            int deleteResult = votesMapper.delete(Wrappers.<VotesEntity>lambdaQuery().eq(VotesEntity::getVoteId, voteId));
            if (deleteResult > 0) {
                result.add(voteId);
            }
        }
        if (CollectionUtils.isEmpty(result)) {
            throw new BusinessException(BizErrorCode.BIZ_DELETE_FAILED);
        }
        return result;
    }

    @Override
    public VotesUpdateRspVO update(VotesUpdateConsoleRepVO repVO, Integer accountId) {
        String roomId = "";
        if (StringUtils.isNotBlank(repVO.getRoomId())) {
            RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(repVO.getRoomId());
            if (roomsEntityDTO == null) {
                throw new BusinessException(BizErrorCode.EMPTY_ROOM);
            }
            roomId = roomsEntityDTO.getRoomId();
            if (ObjectUtils.notEqual(accountId, roomsEntityDTO.getAccountId())) {
                roomId = "";
            }
        }
        VotesUpdateRepVO votesUpdateRepVO = new VotesUpdateRepVO();
        BeanUtils.copyProperties(repVO, votesUpdateRepVO);
        votesUpdateRepVO.setLimitTime(Integer.valueOf(repVO.getLimitTime()));
        votesUpdateRepVO.setRoomId(roomId);
        votesUpdateRepVO.setAccountId(accountId);
        return voteService.update(votesUpdateRepVO);
    }

    @Override
    public PageVO<VotesListRspVO> list(VotesListReqVO reqVO, Integer accountId) {
        List<VotesListRspVO> votesListRspVos;
        int curPage = reqVO.getPage() == null ? 1 : reqVO.getPage();
        int pageSize = reqVO.getPagesize() == null ? 10 : reqVO.getPagesize();
        reqVO.setPage(curPage);
        reqVO.setPagesize(pageSize);

        Token token = TokenThreadLocal.getToken();
        if (token.getRoleType() == null){
            reqVO.setAccountId(null);
        }else if (token.getRoleType() == 1){
            reqVO.setAccountId(null);
        }else if (token.getRoleType() == 2) {
            LambdaQueryWrapper<AccountEntity> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(AccountEntity::getOrgCode, token.getOrgCode());
            List<AccountEntity> accountEntities = accountMapper.selectList(queryWrapper);
            List<Integer> accountIds = accountEntities.stream().map(AccountEntity::getAccountId).collect(Collectors.toList());
            reqVO.setAccountIds(accountIds);
            reqVO.setAccountId(null);
        }
        int count = votesMapper.listCount(reqVO);
        votesListRspVos = votesMapper.list(reqVO);
        // 发布时间
        if (reqVO.getPublish() != null) {
            votesListRspVos = votesListRspVos.stream().map(r -> {
                int second = (int) r.getFinishTime().toInstant(ZoneOffset.ofHours(8)).getEpochSecond();
                String publishTime = DateUtils.timestampFormat(second, r.getLimitTime());
                r.setPublishTime(publishTime);
                return r;
            }).collect(Collectors.toList());
        }
        // 查询是否回答过问卷
        if (reqVO.getAnswerAccountId() != null) {
            RoomJoinsEntityDTO roomJoinInfo = roomInnerService.getRoomJoinInfo(reqVO.getAnswerAccountId(), reqVO.getRoomId());
            List<Long> voteIdList = votesListRspVos.stream().map(VotesListRspVO::getVoteId).collect(Collectors.toList());
            LambdaQueryWrapper<VoteAnswersEntity> where = Wrappers.lambdaQuery();
            where.eq(VoteAnswersEntity::getRoomId, reqVO.getRoomId())
                    .eq(VoteAnswersEntity::getJoinId, roomJoinInfo.getJoinId())
                    .in(!voteIdList.isEmpty(), VoteAnswersEntity::getVoteId, voteIdList);
            List<VoteAnswersEntity> answersList = voteAnswersMapper.selectList(where);
            Map<Long, VoteAnswersEntity> answerMap = answersList.stream().collect(Collectors.toMap(VoteAnswersEntity::getVoteId, alk -> alk));
            votesListRspVos = votesListRspVos.stream().map(r -> {
                Integer answer = answerMap.get(r.getVoteId()) == null ? 0 : 1;
                Integer answerId = answerMap.get(r.getVoteId()) == null ? 0 : Math.toIntExact(answerMap.get(r.getVoteId()).getAnswerId());
                r.setAnswer(answer);
                r.setAnswerId(answerId);
                return r;
            }).collect(Collectors.toList());
        }
        if (StringUtils.isNotBlank(reqVO.getFromRoomId()) && votesListRspVos != null) {
            List<Long> voteIdList = votesListRspVos.stream().map(VotesListRspVO::getVoteId).collect(Collectors.toList());
            LambdaQueryWrapper<RoomVoteLkEntity> where = Wrappers.lambdaQuery();
            where.eq(RoomVoteLkEntity::getRoomId, reqVO.getFromRoomId())
                    .in(!voteIdList.isEmpty(), RoomVoteLkEntity::getVoteId, voteIdList);
            List<RoomVoteLkEntity> lkList = roomVoteLkMapper.selectList(where);
            Map<Long, RoomVoteLkEntity> lkMap = lkList.stream().collect(Collectors.toMap(RoomVoteLkEntity::getVoteId, lk -> lk));
            votesListRspVos = votesListRspVos.stream().map(r -> {
                Integer publish = lkMap.get(r.getVoteId()) == null ? 0 : lkMap.get(r.getVoteId()).getPublish();
                Integer bind = lkMap.get(r.getVoteId()) == null ? 0 : lkMap.get(r.getVoteId()).getBind();
                Integer answer = lkMap.get(r.getVoteId()) == null ? 0 : 1;
                r.setPublish(publish);
                r.setBind(bind);
                r.setAnswer(answer);
                return r;
            }).collect(Collectors.toList());
        }
        if (votesListRspVos == null) {
            return new PageVO<>(curPage, 0, count, null);
        }
        votesListRspVos.forEach(votesListRspVO -> {
            if (StrUtil.isNotEmpty(votesListRspVO.getAccountId())){
                AccountEntity accountEntity = accountMapper.selectById(Integer.parseInt(votesListRspVO.getAccountId()));
                votesListRspVO.setCreateBy(accountEntity.getNickname());
            }
        });
        return new PageVO<>(curPage, votesListRspVos.size(), count, votesListRspVos);
    }

    @Override
    public VoteInfoRspVO info(VoteInfoReqVO voteInfoVO, Integer accountId) {
        if (StringUtils.isBlank(voteInfoVO.getRoomId())) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_PARAM);
        }
        RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(voteInfoVO.getRoomId());
        if (roomsEntityDTO == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        return voteService.info(voteInfoVO, accountId);
    }

    /**
     * 投票统计详情
     *
     * @param reqVO
     * @return
     */
    @Override
    public PageVO<StatRsqVO> stat(StatReqVO reqVO) {
        PageVO<StatRsqVO> pageVO;
        RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (roomsEntityDTO == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        int pageSize = reqVO.getPageSize() == null ? 10 : reqVO.getPageSize();
        int offset = reqVO.getPage() == null ? 0 : (reqVO.getPage() - 1) * pageSize;
        List<StatRsqVO> statRsqVos = roomVoteLkMapper.selectPublishList(reqVO.getRoomId(), reqVO.getBeginTime(), reqVO.getEndTime(), offset, pageSize);
        if (CollectionUtils.isEmpty(statRsqVos)) {
            pageVO = new PageVO<>(reqVO.getPage(), 0, 0, null);
            return pageVO;
        }
        int total = roomVoteLkMapper.selectPublishCount(reqVO.getRoomId(), reqVO.getBeginTime(), reqVO.getEndTime(), offset, pageSize);
        pageVO = new PageVO<>(reqVO.getPage(), statRsqVos.size(), total, statRsqVos);
        List<Long> voteIds = statRsqVos.stream().map(StatRsqVO::getVoteId).collect(Collectors.toList());
        QueryWrapper<VoteAnswersEntity> wrappers = Wrappers.query();
        wrappers.in(VOTE_ID_COLUMN, voteIds)
                .isNull("deleted_at")
                .groupBy(VOTE_ID_COLUMN)
                .select("vote_id, count(*) as c");
        List<Map<String, Object>> voteAnswerCount = voteAnswersMapper.selectMaps(wrappers);
        Map<Long, Integer> answerCount = new HashMap<>(20);
        voteAnswerCount.forEach(x -> answerCount.put(Long.parseLong(x.get(VOTE_ID_COLUMN) + ""), Integer.parseInt(x.get("c") + "")));
        statRsqVos.forEach(x -> x.setCount(answerCount.getOrDefault(x.getVoteId(), 0)));
        return pageVO;
    }

    @Override
    public Boolean export(VoteExportVO reqVO) {
        RoomVoteLkEntity roomVoteLkEntity = roomVoteLkMapper.selectById(reqVO.getId());
        if (roomVoteLkEntity == null) {
            throw new BusinessException(BizErrorCode.COMP_VOTE_INVALID);
        }
        VotesEntity votesEntity = votesMapper.selectById(roomVoteLkEntity.getVoteId());
        if (votesEntity == null) {
            throw new BusinessException(BizErrorCode.COMP_VOTE_NOT_EXIST);
        }
        Token token = TokenThreadLocal.getToken();
        Integer accountId = token.getAccountId();
        RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (roomsEntityDTO == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        String fileName = roomsEntityDTO.getSubject() + "-" + votesEntity.getTitle() + "-" +
                DateUtil.format(LocalDateTime.now(), "yyyyMMdd") + Math.round((Math.random() + 1) * 1000);
        ExportEntity ee = new ExportEntity();
        String[] header = {"直播ID", "直播名称", "投票标题", "投票截止时间"};
        Map<String, Object> param = new HashMap<>(4);
        param.put("il_id", roomsEntityDTO.getIlId());
        param.put(VOTE_ID_COLUMN, roomVoteLkEntity.getVoteId());
        param.put("room_id", roomsEntityDTO.getRoomId());
        param.put("room_name", roomsEntityDTO.getSubject());

        ee.setIlId(roomsEntityDTO.getIlId());
        ee.setRoomId(roomsEntityDTO.getRoomId());
        ee.setAccountId(accountId);
        ee.setExt("csv");
        ee.setExport(reqVO.getExport());
        ee.setSourceId(roomVoteLkEntity.getVoteId() + "");
        ee.setFileName(fileName);
        ee.setTitle(JSON.toJSONString(header));
        ee.setParams(JSON.toJSONString(param));
        ee.setCallback("voteService:voteExport");
        exportService.createExport(ee);
        return true;

    }
}
